home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / Managed / Direct3D / PrtPerVertex / PrtMesh.cs < prev    next >
Encoding:
Text File  |  2004-09-27  |  38.0 KB  |  873 lines

  1. //--------------------------------------------------------------------------------------
  2. // File: PrtMesh.cs
  3. //
  4. // Copyright (c) Microsoft Corporation. All rights reserved.
  5. //--------------------------------------------------------------------------------------
  6.  
  7. //#define DEBUG_VS   // Uncomment this line to debug vertex shaders 
  8. //#define DEBUG_PS   // Uncomment this line to debug pixel shaders 
  9.  
  10. using System;
  11. using System.Collections;
  12. using System.Drawing;
  13. using System.IO;
  14. using System.Text;
  15. using Microsoft.DirectX;
  16. using Microsoft.DirectX.Direct3D;
  17. using Microsoft.Samples.DirectX.UtilityToolkit;
  18.  
  19. namespace PrtPerVertexSample
  20. {
  21.     /// <summary>
  22.     /// The PrtMesh Exception
  23.     /// </summary>
  24.     public class PrtMeshException: DirectXSampleException
  25.     {
  26.         public PrtMeshException() : base("Exception in the PrtMesh class.") {}
  27.         public PrtMeshException(string errorDescription) : base(errorDescription) {}
  28.         public PrtMeshException(Exception inner) : base("Exception in the PrtMesh class.", inner) {}
  29.     }
  30.  
  31.     class PrtMesh
  32.     {
  33.         private struct ReloadState
  34.         {
  35.             public bool  UseReloadState;
  36.             public bool  LoadCompressed;
  37.             public string MeshFileName;
  38.             public string PrtBufferFileName;
  39.             public CompressionQuality Quality;
  40.             public int NumberClusters;
  41.             public int NumberPcaVectors;
  42.         };
  43.  
  44.         #region Constants
  45.         // These constants are described in the article by Peter-Pike Sloan titled 
  46.         // "Efficient Evaluation of Irradiance Environment Maps" in the book 
  47.         // "ShaderX 2 - Shader Programming Tips and Tricks" by Wolfgang F. Engel.
  48.         private static readonly float SqrtPI = (float) Math.Sqrt(Math.PI);
  49.         private static readonly float C0 = 1.0f / (2.0f * SqrtPI);
  50.         private static readonly float C1 = (float) Math.Sqrt(3.0f) / (3.0f * SqrtPI);
  51.         private static readonly float C2 = (float) Math.Sqrt(15.0f) / (8.0f * SqrtPI);
  52.         private static readonly float C3 = (float) Math.Sqrt(5.0f) /(16.0f * SqrtPI);
  53.         private static readonly float C4 = 0.5f * C2;
  54.         #endregion
  55.  
  56.         #region Instance Data
  57.         private Mesh mesh = null;
  58.         private ArrayList albedoTextures = new ArrayList();
  59.         private ExtendedMaterial[] materials = null;
  60.         private float objectRadius = 0.0f;
  61.         private Vector3 objectCenter = new Vector3(0, 0, 0);
  62.  
  63.         private PrtBuffer prtBuffer = null;
  64.         private PrtCompressedBuffer prtCompBuffer = null;
  65.         
  66.         private Effect ndotlEffect = null;
  67.         private Effect prtEffect = null;
  68.         private Effect shIrradEnvMapEffect = null;
  69.         private int order = 0;
  70.  
  71.         private ReloadState reloadState;
  72.  
  73.         // The basis buffer is a large array of floats where 
  74.         // Call D3DXSHPrtCompExtractBasis() to extract the basis 
  75.         // for every cluster.  The basis for a cluster is an array of
  76.         // (NumberPcaVectors + 1) * (NumberChannels * order^2) floats.
  77.         // The "1+" is for the cluster mean.
  78.         private float[] clusterBases = null;
  79.  
  80.         // prtConstants stores the incident radiance dotted with the transfer function.
  81.         // Each cluster has an array of floats which is the size of 
  82.         // 4 + MaxNumberChannels * NumberPcaVectors . This number comes from: there can 
  83.         // be up to 3 channels (R,G,B), and each channel can 
  84.         // have up to NumberPcaVectors of Pca vectors.  Each cluster also has 
  85.         // a mean Pca vector which is described with 4 floats (and hence the +4).
  86.         private float[] prtConstants = null;
  87.         #endregion
  88.  
  89.         #region Properties
  90.         public float Radius { get { return objectRadius; } set { objectRadius = value; } }
  91.         public Vector3 Center { get { return objectCenter; } set { objectCenter = value; } }
  92.         
  93.         public Texture AlbedoTexture { get { return (Texture) albedoTextures[0]; } }
  94.         public ExtendedMaterial[] Materials { get { return materials; } }
  95.         public PrtCompressedBuffer CompressedBuffer { get { return prtCompBuffer; } }
  96.         public int Order { get { return order; } }
  97.  
  98.         public bool IsMeshLoaded { get { return mesh != null; } }
  99.         public bool IsUncompressedBufferLoaded { get { return prtBuffer != null; } }
  100.         public bool IsCompBufferLoaded { get { return prtCompBuffer != null; } }
  101.         public bool IsShaderDataExtracted { get { return clusterBases != null; } }
  102.         public bool IsPrtEffectLoaded { get { return prtEffect != null; } }
  103.         #endregion
  104.  
  105.         public Mesh Mesh { get{ return mesh;} } 
  106.  
  107.         public void SetMesh(Device device, Mesh value) 
  108.         {    // Release any previous mesh object
  109.             if(mesh != null)
  110.             {
  111.                 mesh.Dispose();
  112.                 mesh = null;
  113.             }
  114.  
  115.             mesh = AdjustMeshDecl( device, value);
  116.  
  117.             // Sort the attributes
  118.             int[] adjacency = new int[mesh.NumberFaces * 3];
  119.             mesh.ConvertPointRepsToAdjacency(adjacency);
  120.  
  121.             mesh.OptimizeInPlace(MeshFlags.OptimizeVertexCache | MeshFlags.OptimizeAttributeSort | MeshFlags.OptimizeIgnoreVerts, 
  122.                 adjacency);
  123.         } 
  124.         
  125.         public void Dispose()
  126.         {
  127.             if(mesh != null)
  128.             {
  129.                 mesh.Dispose();
  130.                 mesh = null;
  131.             }
  132.  
  133.             if(prtCompBuffer != null)
  134.             {
  135.                 prtCompBuffer.Dispose();
  136.                 prtCompBuffer = null;
  137.             }
  138.  
  139.             if(prtBuffer != null)
  140.             {
  141.                 prtBuffer.Dispose();
  142.                 prtBuffer = null;
  143.             }
  144.         }
  145.  
  146.         public void RenderWithPrt( Device device, Matrix worldViewProjection, bool isRenderWithAlbedoTexture )
  147.         {
  148.             prtEffect.SetValue( "worldViewProjection", worldViewProjection );
  149.  
  150.             bool hasAlbedoTexture = false;
  151.             for(int i = 0; i < albedoTextures.Count; i++ )
  152.             {
  153.                 
  154.                 if( albedoTextures[i] != null)
  155.                 {
  156.                     hasAlbedoTexture = true;
  157.                     break;
  158.                 }
  159.             }
  160.             if( !hasAlbedoTexture )
  161.                 isRenderWithAlbedoTexture = false;
  162.  
  163.             prtEffect.Technique = isRenderWithAlbedoTexture ? "RenderWithPrtColorLights"
  164.                                                             : "RenderWithPrtColorLightsNoAlbedo";
  165.  
  166.             if( !isRenderWithAlbedoTexture )
  167.             {
  168.                 prtEffect.SetValue( "MaterialDiffuseColor", PrtPerVertex.White);
  169.             }
  170.  
  171.             int cPasses = prtEffect.Begin(0);
  172.  
  173.             for (int pass = 0; pass < cPasses; pass++)
  174.             {
  175.                 prtEffect.BeginPass(pass);
  176.  
  177.                 int attributeTableLength = mesh.GetAttributeTable().Length;
  178.                 for( int i = 0; i < attributeTableLength ; i++ )
  179.                 {            
  180.                     if( isRenderWithAlbedoTexture )
  181.                     {
  182.                         if( albedoTextures.Count > i )
  183.                             prtEffect.SetValue( "AlbedoTexture", albedoTextures[i] as Texture );
  184.  
  185.                         prtEffect.SetValue( "MaterialDiffuseColor", materials[i].Material3D.DiffuseColor);
  186.                         prtEffect.CommitChanges();
  187.                     }
  188.                     mesh.DrawSubset(i);
  189.                 }
  190.  
  191.                 prtEffect.EndPass();
  192.             }
  193.  
  194.             prtEffect.End();
  195.         }
  196.  
  197.         public void RenderWithSHIrradEnvMap( Device device, Matrix worldViewProjection, bool isRenderWithAlbedoTexture )
  198.         {
  199.             shIrradEnvMapEffect.SetValue( "worldViewProjection", worldViewProjection );
  200.  
  201.             bool hasAlbedoTexture = false;
  202.             for(int i = 0; i < albedoTextures.Count; i++ )
  203.             {
  204.                 if( albedoTextures[i] != null )
  205.                 {
  206.                     hasAlbedoTexture = true;
  207.                     break;
  208.                 }
  209.             }
  210.             if( !hasAlbedoTexture )
  211.                 isRenderWithAlbedoTexture = false;
  212.  
  213.             shIrradEnvMapEffect.Technique = isRenderWithAlbedoTexture ? "RenderWithSHIrradEnvMap"
  214.                                                                     : "RenderWithSHIrradEnvMapNoAlbedo";
  215.  
  216.             if( !isRenderWithAlbedoTexture )
  217.             {
  218.                 shIrradEnvMapEffect.SetValue("MaterialDiffuseColor", PrtPerVertex.White);
  219.             }
  220.  
  221.             int cPasses = shIrradEnvMapEffect.Begin(0);
  222.  
  223.             for (int pass = 0; pass < cPasses; pass++)
  224.             {
  225.                 shIrradEnvMapEffect.BeginPass(pass);
  226.  
  227.                 int attributeTableLength = mesh.GetAttributeTable().Length;
  228.                 for( int i = 0; i < attributeTableLength ; i++ )
  229.                 {
  230.                     if( isRenderWithAlbedoTexture )
  231.                     {
  232.                         if( albedoTextures.Count > i )
  233.                             shIrradEnvMapEffect.SetValue( "AlbedoTexture", albedoTextures[i] as Texture );
  234.                         shIrradEnvMapEffect.SetValue( "MaterialDiffuseColor", ColorValue.FromColor(materials[i].Material3D.Diffuse) );
  235.                         shIrradEnvMapEffect.CommitChanges();
  236.                     }
  237.                     mesh.DrawSubset(i);
  238.                 }
  239.  
  240.                 shIrradEnvMapEffect.EndPass();
  241.             }
  242.  
  243.             shIrradEnvMapEffect.End();
  244.         }
  245.  
  246.         public void RenderWithNDotL( Device device, Matrix worldViewProjection, Matrix worldInv, bool isRenderWithAlbedoTexture, DirectionWidget[] lightControl, int numberLights, float lightScale )
  247.         {
  248.             ndotlEffect.SetValue( "worldViewProjection", worldViewProjection );
  249.             ndotlEffect.SetValue( "worldInv", worldInv );
  250.  
  251.             Vector4[] lightDir = new Vector4[PrtPerVertex.MaximumLights];
  252.             Vector4[] lightsDiffuse = new Vector4[PrtPerVertex.MaximumLights];
  253.             Vector4 lightOn = new Vector4(1,1,1,1);
  254.             Vector4 lightOff = new Vector4(0,0,0,0);
  255.             lightOn *= lightScale;
  256.  
  257.             for( int i = 0; i < numberLights; i++ )
  258.                 lightDir[i] = new Vector4( lightControl[i].LightDirection.X, lightControl[i].LightDirection.Y, lightControl[i].LightDirection.Z, 0 );
  259.             for( int i = 0; i < PrtPerVertex.MaximumLights; i++ )
  260.                 lightsDiffuse[i] = (numberLights > i) ? lightOn : lightOff;
  261.  
  262.             bool hasAlbedoTexture = false;
  263.             for(int i = 0; i < albedoTextures.Count; i++ )
  264.             {
  265.                 if( albedoTextures[i] != null )
  266.                 {
  267.                     hasAlbedoTexture = true;
  268.                     break;
  269.                 }
  270.             }
  271.             if( !hasAlbedoTexture )
  272.                 isRenderWithAlbedoTexture = false;
  273.  
  274.             ndotlEffect.Technique = isRenderWithAlbedoTexture ? "RenderWithNDotL"
  275.                 : "RenderWithNDotLNoAlbedo";
  276.  
  277.             if( isRenderWithAlbedoTexture )
  278.             {
  279.                 ndotlEffect.SetValue( "MaterialDiffuseColor", PrtPerVertex.White);
  280.             }
  281.  
  282.             int cPasses = ndotlEffect.Begin(0);
  283.  
  284.             for (int pass = 0; pass < cPasses; pass++)
  285.             {
  286.                 ndotlEffect.BeginPass(pass);
  287.  
  288.                 // 10 and 20 are the register constants
  289.                 device.SetVertexShaderConstant( 10, lightDir);
  290.                 device.SetVertexShaderConstant( 20, lightsDiffuse);
  291.  
  292.                 int attributeTableLength = mesh.GetAttributeTable().Length;
  293.                 for( int i = 0; i < attributeTableLength ; i++ )
  294.                 {
  295.                     if( isRenderWithAlbedoTexture )
  296.                     {
  297.                         if( albedoTextures.Count> i )
  298.                             ndotlEffect.SetValue( "AlbedoTexture", albedoTextures[i] as Texture );
  299.                         ndotlEffect.SetValue( "MaterialDiffuseColor", ColorValue.FromColor(materials[i].Material3D.Diffuse) );
  300.                         ndotlEffect.CommitChanges();
  301.                     }
  302.                     mesh.DrawSubset(i);
  303.                 }
  304.  
  305.                 ndotlEffect.EndPass();
  306.             }
  307.  
  308.             ndotlEffect.End();
  309.         }
  310.  
  311.         public void LoadPrtBufferFromFile( string fileName )
  312.         {
  313.             if(prtBuffer != null)
  314.             {
  315.                 prtBuffer.Dispose();
  316.                 prtBuffer = null;
  317.             }
  318.             if(prtCompBuffer != null)
  319.             {
  320.                 prtCompBuffer.Dispose();
  321.                 prtCompBuffer = null;
  322.             }
  323.  
  324.             string mediaFileLongName = Utility.FindMediaFile(fileName);
  325.             reloadState.PrtBufferFileName =  mediaFileLongName;
  326.             prtBuffer = PrtBuffer.FromFile(mediaFileLongName);
  327.             order = GetOrderFromNumCoefficients( prtBuffer.NumberCoefficients);
  328.         }
  329.  
  330.         public void LoadCompPrtBufferFromFile( string fileName )
  331.         {
  332.             if(prtBuffer != null)
  333.             {
  334.                 prtBuffer.Dispose();
  335.                 prtBuffer = null;
  336.             }
  337.             if(prtCompBuffer != null)
  338.             {
  339.                 prtCompBuffer.Dispose();
  340.                 prtCompBuffer = null;
  341.             }
  342.  
  343.             string mediaFileLongName;
  344.             if (File.Exists(fileName))
  345.             {
  346.                 mediaFileLongName = fileName;
  347.             }
  348.             else
  349.                 mediaFileLongName = Utility.FindMediaFile(fileName);
  350.             reloadState.PrtBufferFileName = mediaFileLongName;
  351.  
  352.             prtCompBuffer = PrtCompressedBuffer.FromFile(reloadState.PrtBufferFileName);
  353.             reloadState.UseReloadState = true;
  354.             reloadState.LoadCompressed = true;
  355.             order = GetOrderFromNumCoefficients( prtCompBuffer.NumberCoefficients);
  356.         }
  357.  
  358.         public void LoadEffects( Device device, Caps caps)
  359.         {
  360.             int numberChannels      = prtCompBuffer.NumberChannels;
  361.             int numberClusters      = prtCompBuffer.NumberClusters;
  362.             int numberPcaVectors    = prtCompBuffer.NumberPcaVectors;
  363.  
  364.             // The number of vertex consts need by the shader can't exceed the 
  365.             // amount the HW can support
  366.             int numberVConsts = numberClusters * (1 + numberChannels * numberPcaVectors / 4) + 4;
  367.             if( numberVConsts > caps.MaxVertexShaderConst )
  368.                 throw new PrtMeshException("Number of Vertex shader constants cannot exceed the amount the Hardware can support");
  369.  
  370.             if(prtEffect != null)
  371.             {
  372.                 prtEffect.Dispose();
  373.                 prtEffect = null;
  374.             }
  375.             if(shIrradEnvMapEffect != null)
  376.             {
  377.                 shIrradEnvMapEffect.Dispose();
  378.                 shIrradEnvMapEffect = null;
  379.             }
  380.             if(ndotlEffect != null)
  381.             {
  382.                 ndotlEffect.Dispose();
  383.                 ndotlEffect = null;
  384.             }
  385.  
  386.             Macro[] defines = new Macro[3];
  387.  
  388.             string maximumNumberClusters = string.Format( "{0}", numberClusters );
  389.             string maximumNumberPcaVectors = string.Format("{0}", numberPcaVectors );
  390.             defines[0].Name = "NumberClusters";
  391.             defines[0].Definition = maximumNumberClusters;
  392.             defines[1].Name = "NumberPcaVectors";
  393.             defines[1].Definition = maximumNumberPcaVectors;
  394.             defines[2].Name = String.Empty;
  395.             defines[2].Definition = String.Empty;
  396.  
  397.             // Define DEBUG_VS and/or DEBUG_PS to debug vertex and/or pixel shaders with the shader debugger.  
  398.             // Debugging vertex shaders requires either REF or software vertex processing, and debugging 
  399.             // pixel shaders requires REF.  The "ShaderFlags.Force{Pixel/Vertex}ShaderSoftwareNoOptimizations" flag improves the debug 
  400.             // experience in the shader debugger.  It enables source level debugging, prevents instruction 
  401.             // reordering, prevents dead code elimination, and forces the compiler to compile against the next 
  402.             // higher available software target, which ensures that the unoptimized shaders do not exceed 
  403.             // the shader model limitations.  Setting these flags will cause slower rendering since the shaders 
  404.             // will be unoptimized and forced into software.  See the DirectX documentation for more information 
  405.             // about using the shader debugger.
  406.             ShaderFlags shaderFlags = ShaderFlags.None;
  407.             #if(DEBUG_VS)
  408.                 shaderFlags |= ShaderFlags.ForceVertexShaderSoftwareNoOptimizations;
  409.             #endif
  410.             #if(DEBUG_PS)
  411.                 shaderFlags |= ShaderFlags.ForcePixelShaderSoftwareNoOptimizations;
  412.             #endif
  413.         
  414.             // Read the effect file
  415.             string mediaFileLongName = Utility.FindMediaFile("PRTColorLights.fx");
  416.  
  417.             // If this fails, there should be debug output
  418.             // that the .fx file failed to compile
  419.             string errors;
  420.             prtEffect = Effect.FromFile(device, mediaFileLongName, defines, null, shaderFlags, null, out errors);
  421.             if ( (errors != null) && (errors.Length > 0) )
  422.                 throw new InvalidOperationException(errors);
  423.  
  424.             // Make sure the technique works on this card
  425.             prtEffect.ValidateTechnique("RenderWithPrtColorLights");
  426.  
  427.             mediaFileLongName = Utility.FindMediaFile("SimpleLighting.fx");
  428.             ndotlEffect = Effect.FromFile(device, mediaFileLongName, null, shaderFlags, null);
  429.  
  430.             mediaFileLongName = Utility.FindMediaFile("SHIrradianceEnvMap.fx");
  431.             shIrradEnvMapEffect = Effect.FromFile(device, mediaFileLongName, null, shaderFlags, null);
  432.         }
  433.    
  434.         public void ComputeSHIrradEnvMapConstants( float[] shCoefficientsRed, float[] shCoefficientsGreen, float[] shCoefficientsBlue )
  435.         {
  436.             float[][] light = { shCoefficientsRed, shCoefficientsGreen, shCoefficientsBlue };
  437.  
  438.             // Lighting environment coefficients
  439.             Vector4[] coefficients = new Vector4[3];
  440.  
  441.             for( int channel = 0; channel < 3; channel++ )
  442.             {
  443.                 coefficients[channel].X = -C1 * light[channel][3];
  444.                 coefficients[channel].Y = -C1 * light[channel][1];
  445.                 coefficients[channel].Z =  C1 * light[channel][2];
  446.                 coefficients[channel].W =  C0 * light[channel][0] - C3 * light[channel][6];
  447.             }
  448.  
  449.             shIrradEnvMapEffect.SetValue("ar", coefficients[0]);
  450.             shIrradEnvMapEffect.SetValue("ag", coefficients[1]);
  451.             shIrradEnvMapEffect.SetValue("ab", coefficients[2]);
  452.  
  453.             for( int channel = 0; channel < 3; channel++ )
  454.             {
  455.                 coefficients[channel].X =        C2 * light[channel][4];
  456.                 coefficients[channel].Y =       -C2 * light[channel][5];
  457.                 coefficients[channel].Z = 3.0f * C3 * light[channel][6];
  458.                 coefficients[channel].W =       -C2 * light[channel][7];
  459.             }
  460.  
  461.             shIrradEnvMapEffect.SetValue("br", coefficients[0]);
  462.             shIrradEnvMapEffect.SetValue("bg", coefficients[1]);
  463.             shIrradEnvMapEffect.SetValue("bb", coefficients[2]);
  464.  
  465.             coefficients[0].X = C4 * light[0][8];
  466.             coefficients[0].Y = C4 * light[1][8];
  467.             coefficients[0].Z = C4 * light[2][8];
  468.             coefficients[0].W = 1.0f;
  469.  
  470.             shIrradEnvMapEffect.SetValue("c", coefficients[0]);
  471.         }
  472.  
  473.         public void ComputeShaderConstants( float[] shCoefficientsRed, float[] shCoefficientsGreen, float[] shCoefficientsBlue, int numberCoefficientsPerChannel )
  474.         {
  475.             System.Diagnostics.Debug.Assert(numberCoefficientsPerChannel == prtCompBuffer.NumberCoefficients);
  476.  
  477.             int numberCoefficients  = prtCompBuffer.NumberCoefficients;
  478.             int order               = this.order;
  479.             int numberChannels      = prtCompBuffer.NumberChannels;
  480.             int numberClusters      = prtCompBuffer.NumberClusters;
  481.             int numberPcaVectors    = prtCompBuffer.NumberPcaVectors;
  482.  
  483.             //
  484.             // With compressed PRT, a single diffuse channel is caluated by:
  485.             //       R[p] = (M[k] dot L') + sum( w[p][j] * (B[k][j] dot L');
  486.             // where the sum runs j between 0 and # of PCA vectors
  487.             //       R[p] = exit radiance at point p
  488.             //       M[k] = mean of cluster k 
  489.             //       L' = source radiance approximated with SH coefficients
  490.             //       w[p][j] = the j'th PCA weight for point p
  491.             //       B[k][j] = the j'th PCA basis vector for cluster k
  492.             //
  493.             // Note: since both (M[k] dot L') and (B[k][j] dot L') can be computed on the CPU, 
  494.             // these values are passed as constants using the array prtConstants.   
  495.             // 
  496.             // So we compute an array of floats, prtConstants, here.
  497.             // This array is the L' dot M[k] and L' dot B[k][j].
  498.             // The source radiance is the lighting environment in terms of spherical
  499.             // harmonic coefficients which can be computed with SphericalHarmonics.Evaluate* or SphericalHarmonics.ProjectCubeMap.
  500.             // M[k] and B[k][j] are also in terms of spherical harmonic basis coefficients 
  501.             // and come from PrtCompressedBuffer.ExtractBasis().
  502.             //
  503.             
  504.             int clusterStride = numberChannels * numberPcaVectors + 4;
  505.             int basisStride = numberCoefficients * numberChannels * (numberPcaVectors + 1);  
  506.  
  507.             float[] clusterBasesSegment = new float[numberCoefficients];
  508.             for( int cluster = 0; cluster < numberClusters; cluster++ ) 
  509.             {
  510.                 // For each cluster, store L' dot M[k] per channel, where M[k] is the mean of cluster k
  511.                 Array.Copy(clusterBases, cluster * basisStride + 0 * numberCoefficients, clusterBasesSegment, 0, numberCoefficients);
  512.                 prtConstants[cluster * clusterStride + 0] = SphericalHarmonics.Dot(order, clusterBasesSegment, shCoefficientsRed);
  513.  
  514.                 Array.Copy(clusterBases, cluster * basisStride + 1 * numberCoefficients, clusterBasesSegment, 0, numberCoefficients);
  515.                 prtConstants[cluster * clusterStride + 1] = SphericalHarmonics.Dot(order, clusterBasesSegment, shCoefficientsGreen);
  516.                 
  517.                 Array.Copy(clusterBases, cluster * basisStride + 2 * numberCoefficients, clusterBasesSegment, 0, numberCoefficients);
  518.                 prtConstants[cluster * clusterStride + 2] = SphericalHarmonics.Dot(order, clusterBasesSegment, shCoefficientsBlue);
  519.                 
  520.                 prtConstants[cluster * clusterStride + 3] = 0.0f;
  521.  
  522.                 // Then per channel we compute L' dot B[k][j], where B[k][j] is the jth PCA basis vector for cluster k
  523.                 int offset1 = cluster * clusterStride + 4;
  524.                 for( int pca = 0; pca < numberPcaVectors; pca++ ) 
  525.                 {
  526.                     int offset2 = cluster * basisStride + (pca + 1) * numberCoefficients * numberChannels;
  527.  
  528.                     Array.Copy(clusterBases, offset2 + 0 * numberCoefficients, clusterBasesSegment, 0, numberCoefficients);
  529.                     prtConstants[offset1 + 0 * numberPcaVectors + pca] = SphericalHarmonics.Dot(order, clusterBasesSegment, shCoefficientsRed);
  530.  
  531.                     Array.Copy(clusterBases, offset2 + 1 * numberCoefficients, clusterBasesSegment, 0, numberCoefficients);
  532.                     prtConstants[offset1 + 1 * numberPcaVectors + pca] = SphericalHarmonics.Dot(order, clusterBasesSegment, shCoefficientsGreen);
  533.                 
  534.                     Array.Copy(clusterBases, offset2 + 2 * numberCoefficients, clusterBasesSegment, 0, numberCoefficients);
  535.                     prtConstants[offset1 + 2 * numberPcaVectors + pca] = SphericalHarmonics.Dot(order, clusterBasesSegment, shCoefficientsBlue);
  536.                 }
  537.             }
  538.  
  539.             prtEffect.SetValue("prtConstants", prtConstants);
  540.         }
  541.  
  542.         public void SetPrtBuffer( PrtBuffer prtBufferLocal, string fileName ) 
  543.         { 
  544.             if(prtBuffer != null)
  545.             {
  546.                 prtBuffer.Dispose();
  547.                 prtBuffer = null;
  548.             }
  549.  
  550.             if(prtCompBuffer != null)
  551.             {
  552.                 prtCompBuffer.Dispose();
  553.                 prtCompBuffer = null;
  554.             }
  555.  
  556.             prtBuffer = prtBufferLocal;
  557.             order = GetOrderFromNumCoefficients( (int) prtBuffer.NumberCoefficients);
  558.  
  559.             reloadState.PrtBufferFileName = fileName;
  560.         }
  561.  
  562.         /// <summary>
  563.         /// Make the mesh have a known decl in order to pass per vertex CPCA 
  564.         /// data to the shader
  565.         /// </summary>
  566.         public Mesh AdjustMeshDecl( Device device, Mesh mesh)
  567.         {
  568.             VertexElement[] declaration =
  569.             {
  570.                 new VertexElement(0,  0,  DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
  571.                 new VertexElement(0,  12, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Normal, 0),
  572.                 new VertexElement(0,  24, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0),
  573.                 new VertexElement(0,  32, DeclarationType.Float1, DeclarationMethod.Default, DeclarationUsage.BlendWeight, 0),
  574.                 new VertexElement(0,  36, DeclarationType.Float4, DeclarationMethod.Default, DeclarationUsage.BlendWeight, 1),
  575.                 new VertexElement(0,  52, DeclarationType.Float4, DeclarationMethod.Default, DeclarationUsage.BlendWeight, 2),
  576.                 new VertexElement(0,  68, DeclarationType.Float4, DeclarationMethod.Default, DeclarationUsage.BlendWeight, 3),
  577.                 new VertexElement(0,  84, DeclarationType.Float4, DeclarationMethod.Default, DeclarationUsage.BlendWeight, 4),
  578.                 new VertexElement(0, 100, DeclarationType.Float4, DeclarationMethod.Default, DeclarationUsage.BlendWeight, 5),
  579.                 new VertexElement(0, 116, DeclarationType.Float4, DeclarationMethod.Default, DeclarationUsage.BlendWeight, 6),
  580.                 VertexElement.VertexDeclarationEnd
  581.             };
  582.             // To do CPCA, we need to store (NumberPcaVectors + 1) scalers per vertex, so 
  583.             // make the mesh have a known decl to store this data.  Since we can't do 
  584.             // skinning and PRT at once, we use DeclarationUsage.BlendWeight[0] 
  585.             // to DeclarationUsage.BlendWeight[6] to store our per vertex data needed for PRT.
  586.             // Notice that DeclarationUsage.BlendWeight[0] is a float1, and
  587.             // DeclarationUsage.BlendWeight[1]-DeclarationUsage.BlendWeight[6] are float4.  This allows 
  588.             // up to 24 PCA weights and 1 float that gives the vertex shader 
  589.             // an index into the vertex's cluster's data
  590.             Mesh outMesh;
  591.             using(mesh)
  592.             {
  593.                 outMesh = mesh.Clone(mesh.Options.Value, declaration, device);
  594.  
  595.                 // Make sure there are normals which are required for lighting
  596.                 if( (mesh.VertexFormat & VertexFormats.Normal) == 0)
  597.                     outMesh.ComputeNormals((GraphicsStream) null);
  598.  
  599.             }
  600.             mesh = null;
  601.  
  602.             return outMesh;
  603.         }
  604.  
  605.         public void LoadMesh(Device device, string meshFileName)
  606.         {
  607.             // Release any previous mesh object
  608.             if(mesh != null)
  609.             {
  610.                 mesh.Dispose();
  611.                 mesh = null;
  612.             }
  613.  
  614.             if(albedoTextures != null)
  615.             {
  616.                 for(int i = 0; i < albedoTextures.Count; i++ )
  617.                 {
  618.                     if(albedoTextures[i] != null)
  619.                     {
  620.                         (albedoTextures[i] as Texture).Dispose();
  621.                     }
  622.                 }
  623.                 albedoTextures.Clear();
  624.             }
  625.  
  626.             // Load the mesh object
  627.             string mediaFileLongName = Utility.FindMediaFile(meshFileName);
  628.             reloadState.MeshFileName = mediaFileLongName;
  629.             mesh = Mesh.FromFile(mediaFileLongName, MeshFlags.Managed, device, out materials);
  630.  
  631.             // Change the current directory to the mesh's directory so we can
  632.             // find the textures.
  633.             string mediaFileDirectory = new FileInfo(mediaFileLongName).DirectoryName;
  634.             string cwd = Directory.GetCurrentDirectory();
  635.             Directory.SetCurrentDirectory( mediaFileDirectory );
  636.  
  637.             // Lock the vertex buffer to get the object's radius & center
  638.             // simply to help position the camera a good distance away from the mesh.
  639.             using(VertexBuffer vertexBuffer = mesh.VertexBuffer)
  640.             {
  641.                 using(GraphicsStream vertices = vertexBuffer.Lock(0, 0, LockFlags.None))
  642.                 {
  643.                     try
  644.                     {
  645.                         // Get declaration, and the size of the decl
  646.                         VertexElement[] decl = mesh.Declaration;
  647.                         int declSize = VertexInformation.GetDeclarationVertexSize(decl, 0);
  648.                         // Calculate the bounding sphere based on this size
  649.                         objectRadius = Geometry.ComputeBoundingSphere( vertices, 
  650.                             mesh.NumberVertices, declSize, out objectCenter);
  651.                     }
  652.                     finally
  653.                     {
  654.                         vertexBuffer.Unlock();
  655.                     }
  656.                 }
  657.             }
  658.  
  659.             // Make the mesh have a known declaration in order to pass per vertex CPCA data to the shader
  660.             mesh = AdjustMeshDecl( device, mesh);
  661.  
  662.             // Optimize the mesh for this graphics card's vertex cache 
  663.             // so when rendering the mesh's triangle list the vertices will 
  664.             // cache hit more often so it won't have to re-execute the vertex shader 
  665.             // on those vertices so it will improve perf.
  666.             int[] adjacency = mesh.ConvertPointRepsToAdjacency(null as GraphicsStream);
  667.             mesh.OptimizeInPlace(MeshFlags.OptimizeVertexCache | MeshFlags.OptimizeAttributeSort | MeshFlags.OptimizeIgnoreVerts, adjacency);
  668.  
  669.             for(int i = 0; i < materials.Length; i++ )
  670.             {
  671.                 // First attempt to look for texture in the same folder as the input folder.
  672.                 // Create the mesh texture from a file
  673.                 try
  674.                 {
  675.                     mediaFileLongName = Utility.FindMediaFile(materials[i].TextureFilename);
  676.                 }
  677.                 catch(MediaNotFoundException)
  678.                 {
  679.                     albedoTextures.Add(null);
  680.                     continue;
  681.                 }
  682.  
  683.                 Texture albedoTexture = ResourceCache.GetGlobalInstance().CreateTextureFromFile(device, mediaFileLongName);
  684.                 albedoTextures.Add(albedoTexture);
  685.             }
  686.    
  687.             Directory.SetCurrentDirectory( cwd );
  688.         }
  689.  
  690.         private int GetOrderFromNumCoefficients( int numberCoefficients )
  691.         {
  692.             int order = 1;
  693.  
  694.             while(order * order < numberCoefficients) 
  695.                 order++;
  696.  
  697.             return order;
  698.         }
  699.  
  700.         public void CompressBuffer( CompressionQuality quality, int numberClusters, int numberPcaVectors)
  701.         {
  702.             System.Diagnostics.Debug.Assert(prtBuffer != null);
  703.  
  704.             if(prtCompBuffer != null)
  705.             {
  706.                 prtCompBuffer.Dispose();
  707.                 prtCompBuffer = null;
  708.             }
  709.             prtCompBuffer = new PrtCompressedBuffer(quality, numberClusters, numberPcaVectors, prtBuffer);
  710.             reloadState.Quality = quality;
  711.             reloadState.NumberClusters = numberClusters;
  712.             reloadState.NumberPcaVectors = numberPcaVectors;
  713.             reloadState.UseReloadState = true;
  714.             reloadState.LoadCompressed = false;
  715.             order = GetOrderFromNumCoefficients( prtBuffer.NumberCoefficients );
  716.         }
  717.  
  718.         public void ExtractCompressedDataForPrtShader() 
  719.         { 
  720.             // First call PrtCompBuffer::NormalizeData.  This isn't nessacary, 
  721.             // but it makes it easier to use data formats that have little presision.
  722.             // It normalizes the Pca weights so that they are between [-1,1]
  723.             // and modifies the basis vectors accordingly.  
  724.             prtCompBuffer.NormalizeData();
  725.  
  726.             int numberSamples= prtCompBuffer.NumberSamples;
  727.             int numberCoefficients = prtCompBuffer.NumberCoefficients;
  728.             int numberChannels = prtCompBuffer.NumberChannels;
  729.             int numberClusters = prtCompBuffer.NumberClusters;
  730.             int numberPcaVectors = prtCompBuffer.NumberPcaVectors;
  731.  
  732.             // With clustered Pca, each vertex is assigned to a cluster.  To figure out 
  733.             // which vertex goes with which cluster, call PrtCompBuffer::ExtractClusterIDs.
  734.             // This will return a cluster ID for every vertex.  Simply pass in an array of UINTs
  735.             // that is the size of the number of vertices (which also equals the number of samples), and 
  736.             // the cluster ID for vertex N will be at puClusterIDs[N].
  737.             int[] clusterIDs = new int[ numberSamples ];
  738.             prtCompBuffer.ExtractClusterIDs( clusterIDs );
  739.  
  740.  
  741.             // Now we also need to store the per vertex PCA weights.  Earilier when
  742.             // the mesh was loaded, we changed the vertex decl to make room to store these
  743.             // PCA weights.  In this sample, we will use DeclarationUsage.BlendWeight[1] to 
  744.             // DeclarationUsage.BlendWeight[6].  Using DeclarationUsage.BlendWeight intead of some other 
  745.             // usage was an arbritatey decision.  Since DeclarationUsage.BlendWeight[1-6] were 
  746.             // declared as float4 then we can store up to 6*4 PCA weights per vertex.  They don't
  747.             // have to be declared as float4, but its a reasonable choice.  So for example, 
  748.             // if NumberPcaVectors=16 the function will write data to DeclarationUsage.BlendWeight[1-4]
  749.             prtCompBuffer.ExtractToMesh( numberPcaVectors, DeclarationUsage.BlendWeight, 1, mesh );
  750.  
  751.             // Extract the cluster bases into a large array of floats.  
  752.             // PrtCompressedBuffer::ExtractBasis will extract the basis 
  753.             // for a single cluster.
  754.             //
  755.             // A single cluster basis is an array of
  756.             // (NumberPcaVectors + 1) * NumberCoefficients * NumberChannels floats
  757.             // The "1+" is for the cluster mean.
  758.             int clusterBasisSize = (numberPcaVectors + 1) * numberCoefficients * numberChannels;
  759.             int bufferSize       = clusterBasisSize * numberClusters; 
  760.  
  761.             clusterBases = new float[bufferSize];
  762.             float[] clusterBasesSegment = new float[clusterBasisSize];
  763.             for( int cluster = 0; cluster < numberClusters; cluster++ ) 
  764.             {
  765.                 // ExtractBasis() extracts the basis for a single cluster at a time.
  766.                 prtCompBuffer.ExtractBasis( cluster, clusterBasesSegment);
  767.                 clusterBasesSegment.CopyTo(clusterBases, cluster * clusterBasisSize);
  768.             }
  769.  
  770.             prtConstants = new float[ numberClusters * (4 + numberChannels * numberPcaVectors) ];
  771.         }
  772.  
  773.         public void OnCreateDevice( Device device)
  774.         {
  775.             if( reloadState.UseReloadState )
  776.             {
  777.                 LoadMesh( device, reloadState.MeshFileName );
  778.                 if( reloadState.LoadCompressed )
  779.                 {
  780.                     LoadCompPrtBufferFromFile( reloadState.PrtBufferFileName );
  781.                 }
  782.                 else
  783.                 {
  784.                     LoadPrtBufferFromFile( reloadState.PrtBufferFileName );
  785.                     CompressBuffer( reloadState.Quality, reloadState.NumberClusters, reloadState.NumberPcaVectors );
  786.                 }
  787.                 ExtractCompressedDataForPrtShader();
  788.                 LoadEffects( device, PrtPerVertex.SampleFramework.DeviceCaps);
  789.             }
  790.         }
  791.  
  792.  
  793.         public void OnResetDevice()
  794.         {
  795.             if( prtEffect != null )
  796.                 prtEffect.OnResetDevice();
  797.  
  798.             if( shIrradEnvMapEffect != null)
  799.                 shIrradEnvMapEffect.OnResetDevice();
  800.  
  801.             if( ndotlEffect != null )
  802.                 ndotlEffect.OnResetDevice();
  803.         }
  804.  
  805.  
  806.         public void OnLostDevice()
  807.         {
  808.             if( prtEffect != null )
  809.                 prtEffect.OnLostDevice();
  810.  
  811.             if( shIrradEnvMapEffect != null)
  812.                 shIrradEnvMapEffect.OnLostDevice();
  813.  
  814.             if( ndotlEffect != null )
  815.                 ndotlEffect.OnLostDevice();
  816.         }
  817.  
  818.  
  819.         public void OnDestroyDevice()
  820.         {
  821.             if( !reloadState.UseReloadState )
  822.                 reloadState = new ReloadState();
  823.  
  824.             if(mesh != null)
  825.             {
  826.                 mesh.Dispose();
  827.                 mesh = null;
  828.             }
  829.             if(albedoTextures != null)
  830.             {
  831.                 for(int i = 0; i < albedoTextures.Count; i++ )
  832.                 {
  833.                     if(albedoTextures[i] != null)
  834.                     {
  835.                         (albedoTextures[i] as Texture).Dispose();
  836.                     }
  837.                 }
  838.                 albedoTextures.Clear();
  839.             }
  840.  
  841.             if( prtEffect != null )
  842.             {
  843.                 prtEffect.Dispose();
  844.                 prtEffect = null;
  845.             }
  846.  
  847.             if( shIrradEnvMapEffect != null)
  848.             {
  849.                 shIrradEnvMapEffect.Dispose();
  850.                 shIrradEnvMapEffect = null;
  851.             }
  852.  
  853.             if( ndotlEffect != null )
  854.             {
  855.                 ndotlEffect.Dispose();
  856.                 ndotlEffect = null;
  857.             }
  858.  
  859.             if( prtBuffer != null)
  860.             {
  861.                 prtBuffer.Dispose();
  862.                 prtBuffer = null;
  863.             }
  864.  
  865.             if( prtCompBuffer != null)
  866.             {
  867.                 prtCompBuffer.Dispose();
  868.                 prtCompBuffer = null;
  869.             }
  870.         }
  871.     }
  872. }
  873.